/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ids.dl;

import ids.util.Config;
import ids.util.ObjectLock;
import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.rmi.CORBA.Util;

/**
 *
 * @author isuru
 */
public class Coordinator {

    List<String> results;
    volatile ArrayList<IServer> servers = null;
    HeartBeat heartBeat;
    HashMap<IServer, ArrayList<String>> assignWorks;
    List<String> workCompletedServers;

    public Coordinator(ArrayList<IServer> servers) {
        this.results = Collections.synchronizedList(new ArrayList<String>());
        this.servers = servers;
        this.heartBeat = new HeartBeat(servers);
        this.workCompletedServers = Collections.synchronizedList(new ArrayList<String>());
        this.assignWorks = new HashMap<IServer, ArrayList<String>>();
    }

    private void assignWork(final ObjectLock objectLock, final IServer stub, final String keyWord, final ArrayList<String> filePaths, final String clientId) {
        assignWorks.put(stub, filePaths);
        new Thread() {
            @Override
            public void run() {
                try {
                    ArrayList<String> resultsPerServer = stub.doSearch(keyWord, filePaths, clientId);

                    //synchronized (objectLock) {
                        for (String resultsPerServer1 : resultsPerServer) {
                            results.add(resultsPerServer1);
                        }
                        workCompletedServers.add(stub.getIp().getHostName());
                        //objectLock.notify();
                    //}
                } catch (RemoteException ex) {
                    Logger.getLogger(Coordinator.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }.start();
    }

    private void assignExtraWork(ArrayList<HeartBeatData> existingServers, final ObjectLock objectLock, final String keyWord, final ArrayList<String> filePaths, final String clientId) {
        double minCPULoadAverage = 0;
        double tempCPULoadAverage = 0;
        int selectedServerId = 0;
        int i = 0;
        for (HeartBeatData existingServer : existingServers) {
            try {
                tempCPULoadAverage = existingServer.getServer().voteForExtraWork();
                if (tempCPULoadAverage >= 0) {
                    if (i == 0) {
                        minCPULoadAverage = tempCPULoadAverage;
                        selectedServerId = i;
                    } else if (tempCPULoadAverage < minCPULoadAverage) {
                        minCPULoadAverage = tempCPULoadAverage;
                        selectedServerId = i;
                    }
                } else {
                    break;
                }
            } catch (RemoteException ex) {
                Logger.getLogger(ClientsHandler.class.getName()).log(Level.SEVERE, null, ex);
            }
            i++;
        }

        if (minCPULoadAverage < 0) {
            Random ran = new Random();
            assignWork(objectLock, existingServers.get(ran.nextInt(existingServers.size())).getServer(), keyWord, filePaths, clientId);
        } else {
            assignWork(objectLock, existingServers.get(selectedServerId).getServer(), keyWord, filePaths, clientId);
        }
    }

    private void checkHeartBeat(final ObjectLock objectLock, final String keyWord, final ArrayList<String> filePaths, final String clientId) {
        for (int i = 0; i < this.heartBeat.getExistingServers().size(); i++) {
            try {
                this.heartBeat.getExistingServers().get(i).getServer().isActive();
                this.heartBeat.getExistingServers().get(i).resetCIHBCount();
            } catch (Exception ex) {
                this.heartBeat.getExistingServers().get(i).incCIHBCount();
                if (this.heartBeat.getExistingServers().get(i).getCIHBCount() >= Config.getMaxCIHBCount()) {
                    this.heartBeat.getExistingServers().remove(i);
                    assignExtraWork(this.heartBeat.getExistingServers(), objectLock, keyWord, filePaths, clientId);
                }
            }
        }
    }

    public void doSearch(final ObjectLock objectLock, final String keyWord, final ArrayList<String> filePaths, final String clientId) throws RemoteException {
        System.out.println("I'm the coordinator... ");
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                checkHeartBeat(objectLock, keyWord, filePaths, clientId);
            }
        }, 1 * 1000, 1 * 1000);

        ArrayList<ArrayList<String>> filePathsPerServer = new ArrayList<ArrayList<String>>();
        
        for(int i = 0; i < servers.size(); i++)
        {
            filePathsPerServer.add(new ArrayList<String>());
        }
        
        for(int i = 0; i < filePaths.size(); i++)
        {
            filePathsPerServer.get(i % servers.size()).add(filePaths.get(i));
        }
        
        for (int i = 0; i < servers.size(); i++) {         
            assignWork(objectLock, servers.get(i), keyWord, filePathsPerServer.get(i), clientId);
        }
        while (workCompletedServers.size() != heartBeat.getExistingServers().size()) {

        }
//        synchronized (objectLock) 
//        {
//            objectLock.notify();
//        }
    }

    public List<String> getResults() {
        return results;
    }
}
